"""
认证相关API接口
"""
from fastapi import APIRouter, Depends, HTTPException, status, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from pydantic import BaseModel, EmailStr
from datetime import datetime, timedelta
from typing import Optional

from core.database import get_db
from core.logging_config import get_logger, log_api_request
from core.exceptions import AuthenticationError, ValidationError
from core.auth import create_access_token, verify_token, get_current_user, get_current_active_user
from models.user import User, UserSession

# 路由器
router = APIRouter()
# 安全认证
security = HTTPBearer()

# 日志记录器
logger = get_logger("petition_auth")


class UserLogin(BaseModel):
    """用户登录模型"""
    username: str
    password: str


class UserRegister(BaseModel):
    """用户注册模型"""
    username: str
    email: EmailStr
    password: str
    full_name: Optional[str] = None
    phone: Optional[str] = None


class TokenResponse(BaseModel):
    """令牌响应模型"""
    access_token: str
    refresh_token: str
    token_type: str
    expires_in: int
    user_info: dict


class PasswordResetRequest(BaseModel):
    """密码重置请求模型"""
    email: EmailStr


class PasswordReset(BaseModel):
    """密码重置模型"""
    token: str
    new_password: str
    confirm_password: str


class PasswordChange(BaseModel):
    """密码修改模型"""
    current_password: str
    new_password: str
    confirm_password: str


@router.post("/login", response_model=TokenResponse)
@log_api_request("user_login")
async def login(user_login: UserLogin, db: Session = Depends(get_db)):
    """用户登录"""
    logger.info(f"用户登录请求: {user_login.username}")
    # 查找用户
    user = db.query(User).filter(
        User.username == user_login.username,
        User.is_deleted == False
    ).first()
    
    if not user:
        logger.warning(f"用户不存在: {user_login.username}")
        raise AuthenticationError("用户名或密码错误")
    
    # 验证密码
    if not user.verify_password(user_login.password):
        logger.warning(f"密码错误: {user_login.username}")
        raise AuthenticationError("用户名或密码错误")
    
    # 检查用户状态
    if not user.is_active:
        raise AuthenticationError("用户已被禁用")
    
    # 更新登录信息
    user.update_login_info()
    db.commit()
    
    # 创建访问令牌
    access_token_expires = timedelta(minutes=60)
    access_token = create_access_token(
        data={"sub": user.username, "user_id": user.id},
        expires_delta=access_token_expires
    )
    
    # 生成刷新令牌
    refresh_token_expires = timedelta(days=7)  # 刷新令牌7天有效期
    refresh_token = create_access_token(
        data={"sub": user.username, "user_id": user.id, "type": "refresh"},
        expires_delta=refresh_token_expires
    )
    
    # 创建用户会话
    user_session = UserSession(
        user_id=user.id,
        session_token=access_token,
        refresh_token=refresh_token,
        expires_at=datetime.utcnow() + access_token_expires,
        refresh_token_expires_at=datetime.utcnow() + refresh_token_expires,
        ip_address="",
        user_agent=""
    )
    db.add(user_session)
    db.commit()
    
    logger.info(f"用户登录成功: {user.username}")
    
    return {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": "bearer",
        "expires_in": 30 * 60,
        "user_info": user.to_dict()
    }


@router.post("/register", response_model=TokenResponse)
@log_api_request("user_register")
async def register(user_register: UserRegister, db: Session = Depends(get_db)):
    """用户注册"""
    logger.info(f"用户注册请求: {user_register.username}")
    
    # 检查用户名是否已存在
    existing_user = db.query(User).filter(
        User.username == user_register.username,
        User.is_deleted == False
    ).first()
    
    if existing_user:
        raise ValidationError("用户名已存在")
    
    # 检查邮箱是否已存在
    existing_email = db.query(User).filter(
        User.email == user_register.email,
        User.is_deleted == False
    ).first()
    
    if existing_email:
        raise ValidationError("邮箱已存在")
    
    # 创建新用户
    user = User(
        username=user_register.username,
        email=user_register.email,
        password=user_register.password,
        full_name=user_register.full_name,
        phone=user_register.phone
    )
    
    db.add(user)
    db.commit()
    db.refresh(user)
    
    # 创建访问令牌
    access_token_expires = timedelta(minutes=30)
    access_token = create_access_token(
        data={"sub": user.username, "user_id": user.id},
        expires_delta=access_token_expires
    )
    
    # 生成刷新令牌
    refresh_token_expires = timedelta(days=7)  # 刷新令牌7天有效期
    refresh_token = create_access_token(
        data={"sub": user.username, "user_id": user.id, "type": "refresh"},
        expires_delta=refresh_token_expires
    )
    
    # 创建用户会话
    user_session = UserSession(
        user_id=user.id,
        session_token=access_token,
        refresh_token=refresh_token,
        expires_at=datetime.utcnow() + access_token_expires,
        refresh_token_expires_at=datetime.utcnow() + refresh_token_expires,
        ip_address="",
        user_agent=""
    )
    db.add(user_session)
    db.commit()
    
    logger.info(f"用户注册成功: {user.username}")
    
    return {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": "bearer",
        "expires_in": 30 * 60,
        "user_info": user.to_dict()
    }


@router.post("/logout")
@log_api_request("user_logout")
async def logout(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """用户登出"""
    logger.info(f"用户登出: {current_user.username}")
    
    # 使所有会话过期
    db.query(UserSession).filter(
        UserSession.user_id == current_user.id,
        UserSession.is_active == True
    ).update({"is_active": False})
    
    db.commit()
    
    return {"message": "登出成功"}


@router.get("/me")
@log_api_request("get_current_user")
async def get_current_user_info(current_user: User = Depends(get_current_user)):
    """获取当前用户信息"""
    return {
        "user": current_user.to_dict(),
        "permissions": current_user.get_permission_codes()
    }


@router.post("/password-reset-request")
@log_api_request("password_reset_request")
async def request_password_reset(
    request: PasswordResetRequest,
    db: Session = Depends(get_db)
):
    """请求密码重置"""
    logger.info(f"密码重置请求: {request.email}")
    
    # 查找用户
    user = db.query(User).filter(
        User.email == request.email,
        User.is_deleted == False
    ).first()
    
    if not user:
        # 为了安全，不提示用户不存在
        return {"message": "密码重置链接已发送到您的邮箱"}
    
    # 生成重置令牌
    reset_token = user.generate_reset_token()
    db.commit()
    
    # 这里应该发送邮件，暂时只返回令牌用于测试
    # 在实际环境中，应该发送邮件包含重置链接
    logger.info(f"密码重置令牌已生成: {user.username}")
    
    return {
        "message": "密码重置链接已发送到您的邮箱",
        "reset_token": reset_token  # 仅用于测试，生产环境中应该删除
    }


@router.post("/password-reset")
@log_api_request("password_reset")
async def reset_password(
    reset_data: PasswordReset,
    db: Session = Depends(get_db)
):
    """重置密码"""
    logger.info("密码重置请求")
    
    # 查找用户
    user = db.query(User).filter(
        User.reset_token == reset_data.token,
        User.is_deleted == False
    ).first()
    
    if not user:
        raise ValidationError("重置令牌无效")
    
    # 检查令牌是否过期
    if not user.is_reset_token_valid():
        raise ValidationError("重置令牌已过期")
    
    # 验证新密码
    if reset_data.new_password != reset_data.confirm_password:
        raise ValidationError("两次输入的密码不一致")
    
    # 更新密码
    user.set_password(reset_data.new_password)
    user.clear_reset_token()
    db.commit()
    
    logger.info(f"密码重置成功: {user.username}")
    
    return {"message": "密码重置成功"}


@router.post("/change-password")
@log_api_request("change_password")
async def change_password(
    password_data: PasswordChange,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """修改密码"""
    logger.info(f"修改密码请求: {current_user.username}")
    
    # 验证当前密码
    if not current_user.verify_password(password_data.current_password):
        raise AuthenticationError("当前密码错误")
    
    # 验证新密码
    if password_data.new_password != password_data.confirm_password:
        raise ValidationError("两次输入的密码不一致")
    
    # 更新密码
    current_user.set_password(password_data.new_password)
    db.commit()
    
    logger.info(f"密码修改成功: {current_user.username}")
    
    return {"message": "密码修改成功"}


@router.post("/validate")
@log_api_request("validate_token")
async def validate_token(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db)
):
    """验证token有效性"""
    try:
        token = credentials.credentials
        payload = verify_token(token)
        username: str = payload.get("sub")
        
        # 查找用户
        user = db.query(User).filter(
            User.username == username,
            User.is_deleted == False
        ).first()
        
        if not user:
            raise AuthenticationError("用户不存在")
        
        if not user.is_active:
            raise AuthenticationError("用户已被禁用")
        
        # 检查用户会话是否有效
        user_session = db.query(UserSession).filter(
            UserSession.session_token == token,
            UserSession.user_id == user.id,
            UserSession.is_active == True
        ).first()
        
        if not user_session:
            raise AuthenticationError("会话已失效")
        
        # 检查会话是否过期
        if user_session.expires_at and user_session.expires_at < datetime.utcnow():
            user_session.is_active = False
            db.commit()
            raise AuthenticationError("会话已过期")
        
        logger.info(f"Token验证成功: {user.username}")
        
        return {
            "valid": True,
            "user": user.to_dict(),
            "permissions": user.get_permission_codes(),
            "expires_in": int((user_session.expires_at - datetime.utcnow()).total_seconds()) if user_session.expires_at else 1800
        }
        
    except AuthenticationError:
        raise
    except Exception as e:
        logger.error(f"Token验证失败: {str(e)}")
        raise AuthenticationError("Token验证失败")


@router.post("/refresh")
@log_api_request("refresh_token")
async def refresh_token(
    request: dict,
    db: Session = Depends(get_db)
):
    """刷新访问令牌"""
    refresh_token = request.get("refresh_token")
    
    if not refresh_token:
        raise AuthenticationError("缺少刷新令牌")
    
    try:
        # 查找有效的刷新令牌会话
        user_session = db.query(UserSession).filter(
            UserSession.refresh_token == refresh_token,
            UserSession.is_active == True,
            UserSession.refresh_token_expires_at > datetime.utcnow()
        ).first()
        
        if not user_session:
            raise AuthenticationError("刷新令牌无效或已过期")
        
        # 查找用户
        user = db.query(User).filter(
            User.id == user_session.user_id,
            User.is_deleted == False,
            User.is_active == True
        ).first()
        
        if not user:
            raise AuthenticationError("用户不存在或已被禁用")
        
        # 使旧会话过期
        user_session.is_active = False
        db.commit()
        
        # 创建新的访问令牌
        access_token_expires = timedelta(minutes=30)
        new_access_token = create_access_token(
            data={"sub": user.username, "user_id": user.id},
            expires_delta=access_token_expires
        )
        
        # 创建新的会话
        new_session = UserSession(
            user_id=user.id,
            session_token=new_access_token,
            refresh_token=refresh_token,  # 保持刷新令牌不变
            expires_at=datetime.utcnow() + access_token_expires,
            refresh_token_expires_at=user_session.refresh_token_expires_at,
            ip_address=user_session.ip_address,
            user_agent=user_session.user_agent
        )
        db.add(new_session)
        db.commit()
        
        logger.info(f"Token刷新成功: {user.username}")
        
        return {
            "access_token": new_access_token,
            "token_type": "bearer",
            "expires_in": 30 * 60,
            "user_info": user.to_dict()
        }
        
    except AuthenticationError:
        raise
    except Exception as e:
        logger.error(f"Token刷新失败: {str(e)}")
        raise AuthenticationError("Token刷新失败")


auth_router = router